//
// * Copyright (c) 2002-2009 "Neo Technology,"
// *     Network Engine for Objects in Lund AB [http://neotechnology.com]
// *
// * This file is part of Neo4j.
// * 
// * Neo4j is free software: you can redistribute it and/or modify
// * it under the terms of the GNU Affero General Public License as
// * published by the Free Software Foundation, either version 3 of the
// * License, or (at your option) any later version.
// * 
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// * GNU Affero General Public License for more details.
// * 
// * You should have received a copy of the GNU Affero General Public License
// * along with this program. If not, see <http://www.gnu.org/licenses/>.
// 
namespace org.neo4j.kernel.impl.core
{


	using HeuristicMixedException = javax.transaction.HeuristicMixedException;
	using HeuristicRollbackException = javax.transaction.HeuristicRollbackException;
	using NotSupportedException = javax.transaction.NotSupportedException;
	using RollbackException = javax.transaction.RollbackException;
	using SystemException = javax.transaction.SystemException;
	using TransactionManager = javax.transaction.TransactionManager;

	using Node = org.neo4j.graphdb.Node;
	using NotFoundException = org.neo4j.graphdb.NotFoundException;
	using RelationshipType = org.neo4j.graphdb.RelationshipType;
	using AdaptiveCacheManager = org.neo4j.kernel.impl.cache.AdaptiveCacheManager;
	using PropertyIndexData = org.neo4j.kernel.impl.nioneo.store.PropertyIndexData;
	using RelationshipTypeData = org.neo4j.kernel.impl.nioneo.store.RelationshipTypeData;
	using IdGenerator = org.neo4j.kernel.impl.persistence.IdGenerator;
	using PersistenceManager = org.neo4j.kernel.impl.persistence.PersistenceManager;
	using LockManager = org.neo4j.kernel.impl.transaction.LockManager;
	using TransactionFailureException = org.neo4j.kernel.impl.transaction.TransactionFailureException;

	public class NeoModule
	{
		private static Logger log = Logger.getLogger(typeof(NeoModule).Name);

		private bool startIsOk = true;

		private const int INDEX_COUNT = 2500;

		private readonly TransactionManager transactionManager;
		private readonly AdaptiveCacheManager cacheManager;
		private readonly LockManager lockManager;
		private readonly IdGenerator idGenerator;

		private NodeManager nodeManager;

		private bool readOnly = false;

		public NeoModule(AdaptiveCacheManager cacheManager, LockManager lockManager, TransactionManager transactionManager, IdGenerator idGenerator)
		{
			this.cacheManager = cacheManager;
			this.lockManager = lockManager;
			this.transactionManager = transactionManager;
			this.idGenerator = idGenerator;
		}

		public NeoModule(AdaptiveCacheManager cacheManager, LockManager lockManager, TransactionManager transactionManager, IdGenerator idGenerator, bool readOnly)
		{
			this.cacheManager = cacheManager;
			this.lockManager = lockManager;
			this.transactionManager = transactionManager;
			this.idGenerator = idGenerator;
			this.readOnly = readOnly;
		}

		public virtual void init()
		{
		}

		public virtual void start(LockReleaser lockReleaser, PersistenceManager persistenceManager, Map<object, object> @params)
		{
			if (!startIsOk)
			{
				return;
			}
			bool useNewCache = true;
			if (@params.containsKey("use_old_cache") && @params.get("use_old_cache").Equals("true"))
			{
				useNewCache = false;
			}
			if (!readOnly)
			{
				nodeManager = new NodeManager(cacheManager, lockManager, lockReleaser, transactionManager, persistenceManager, idGenerator, useNewCache);
			}
			else
			{
				nodeManager = new ReadOnlyNodeManager(cacheManager, lockManager, lockReleaser, transactionManager, persistenceManager, idGenerator, useNewCache);
			}
		// load and verify from PS
			RelationshipTypeData[] relTypes = null;
			PropertyIndexData[] propertyIndexes = null;
			beginTx();
			relTypes = persistenceManager.loadAllRelationshipTypes();
			propertyIndexes = persistenceManager.loadPropertyIndexes(INDEX_COUNT);
			commitTx();
			nodeManager.addRawRelationshipTypes(relTypes);
			nodeManager.addPropertyIndexes(propertyIndexes);
			if (propertyIndexes.Length < INDEX_COUNT)
			{
				nodeManager.setHasAllpropertyIndexes(true);
			}
			nodeManager.Start(@params);
			startIsOk = false;
		}

		private void beginTx()
		{
			try
			{
				transactionManager.begin();
			}
			catch (NotSupportedException e)
			{
				throw new TransactionFailureException("Unable to begin transaction.", e);
			}
			catch (SystemException e)
			{
				throw new TransactionFailureException("Unable to begin transaction.", e);
			}
		}

		private void commitTx()
		{
			try
			{
				transactionManager.commit();
			}
			catch (SecurityException e)
			{
				throw new TransactionFailureException("Failed to commit.", e);
			}
			catch (IllegalStateException e)
			{
				throw new TransactionFailureException("Failed to commit.", e);
			}
			catch (RollbackException e)
			{
				throw new TransactionFailureException("Failed to commit.", e);
			}
			catch (HeuristicMixedException e)
			{
				throw new TransactionFailureException("Failed to commit.", e);
			}
			catch (HeuristicRollbackException e)
			{
				throw new TransactionFailureException("Failed to commit.", e);
			}
			catch (SystemException e)
			{
				throw new TransactionFailureException("Failed to commit.", e);
			}
		}

		public virtual void setReferenceNodeId(int nodeId)
		{
			nodeManager.setReferenceNodeId(nodeId.intValue());
			try
			{
				nodeManager.getReferenceNode();
			}
			catch (NotFoundException e)
			{
				log.warning("Reference node[" + nodeId + "] not valid.");
			}
		}

		public virtual int getCurrentReferenceNodeId()
		{
			try
			{
				return (int) nodeManager.getReferenceNode().getId();
			}
			catch (NotFoundException e)
			{
				return -1;
			}
		}

		public virtual void createNewReferenceNode()
		{
			Node node = nodeManager.createNode();
			nodeManager.setReferenceNodeId((int) node.getId());
		}

		public virtual void reload(Map<object, object> @params)
		{
			throw new UnsupportedOperationException();
		}

		public virtual void stop()
		{
			nodeManager.clearPropertyIndexes();
			nodeManager.clearCache();
			nodeManager.Stop();
		}

		public virtual void destroy()
		{
		}

		public virtual NodeManager getNodeManager()
		{
			return this.nodeManager;
		}

		public virtual IEnumerable <RelationshipType> getRelationshipTypes()
		{
			return nodeManager.getRelationshipTypes();
		}
	}
}